var clone = function (objfrom, option, pattern, queuefrom, queueto) {
    // handle value-type
    switch (typeof(objfrom)) {
        case 'string':
        case 'number':
        case 'boolean':
        case 'symbol':
        case 'undefined':
            return objfrom;
    }
    // handle null
    if (objfrom === null)
        return null;

    option = option instanceof Object ? option : {};
    option.withoutFunction = typeof(option.withoutFunction) === 'boolean' ? option.withoutFunction : false;
    option.withoutCircular = typeof(option.withoutCircular) === 'boolean' ? option.withoutCircular : false;

    // handle circular-reference
    queuefrom = queuefrom || [];
    queueto = queueto || [];
    for (let i = 0; i < queuefrom.length; i++) {
        if (queuefrom[i] === objfrom) {
            return option.withoutCircular ? undefined : queueto[i];
        }
    }

    // handle common ref-Class
    var objto = undefined;
    switch (objfrom.constructor) {
        case Function: // Function is assign by Varible-Reference, not clone, but without influence
            if (option.withoutFunction)
                return undefined;
            objto = eval('(' + objfrom.toString() + ')');
            break;
        case Object:
            objto = {};
            break;
        case Array:
            objto = [];
            break;
    }

    // handle other ref-Class
    if (objto === undefined) {
        try {
            objto = new objfrom.constructor(objfrom.valueOf()); // Date, RegExp, ...
        } catch (err) {
            objto = undefined;
        }
    }
    if (objto === undefined){
        try {
            objto = new objfrom.constructor();
        } catch (err) {
            objto = undefined;
        }
    }
    if (objto === undefined)
        objto = {};

    // recursive clone ref-Class's members
    queuefrom.push(objfrom);
    queueto.push(objto);
    pattern = pattern ? pattern : 0;
    if (pattern instanceof Object) {
        for (let i in pattern) {
            if (objfrom[i] !== undefined) {
                var val = clone(objfrom[i], option, pattern[i], queuefrom, queueto);
                if (val !== undefined)
                    objto[i] = val;
            }
        }
        for (let i in objto){
            if (i !== undefined && pattern[i] === undefined)
                delete objto[i];
        }
    } else {
        for (let i in objfrom) {
            if (objfrom[i] !== undefined) {
                var val = clone(objfrom[i], option, pattern[i], queuefrom, queueto);
                if (val !== undefined)
                    objto[i] = val;
            }
        }
    }
    queueto.pop();
    queuefrom.pop();

    return objto;
};

var testClone = function () {
    var class1 = function () {
        this.classProperty = 'abcde';
    };
    var a = {};
    a.bb = a;
    a.cc = {dd: a, ee: new Date(), ff: function(){}, gg: Date};
    a.cc.ee.hh = 'aa';
    a.cc.ff.ii = a;
    a.jj = new class1();
    a.kk = ['asdf', 'fdsa'];
    a.ll = 0xFAD21;
    a.mm = /^Hello$/gm;
    console.log("----------------origobject----------------");
    console.log(a);
    console.log("----------------cloneobject----------------");
    console.log(clone(a));
    console.log("---cloneobject without function and circulation----");
    console.log( clone(a, {withoutFunction:true,withoutCircular:true}) );
    console.log("----------cloneobject by pattern-----------");
    console.log( clone(a, null, {bb: 0, cc: {dd: 0, ee: 0, ff: {ii: 0}, gg: 0}, jj: 0, kk: 0, ll: 0}) );
};

module.exports.clone = clone;
module.exports.testClone = testClone;